package jp.dodododo.dao.handler.impl;

import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import jp.dodododo.dao.IterationCallback;
import jp.dodododo.dao.columns.ResultSetColumn;
import jp.dodododo.dao.handler.ResultSetHandler;
import jp.dodododo.dao.types.JavaType;
import jp.dodododo.dao.types.JavaTypes;
import jp.dodododo.dao.types.SQLTypes;
import jp.dodododo.dao.util.DbArrayList;
import jp.dodododo.dao.util.TypesUtil;

/**
 * 
 * @author Satoshi Kimura
 */
public abstract class AbstractResultSetHandler<T> implements ResultSetHandler<T> {
	private IterationCallback<T> callback;

	public AbstractResultSetHandler(IterationCallback<T> callback) {
		this.callback = callback;
	}

	public void handle(ResultSet rs) throws SQLException {
		List<ResultSetColumn> resultSetColumnList = getResultSetColumnList(rs.getMetaData());
		StringBuilder headerStr = createHeaderString(resultSetColumnList);
		List<T> resultList = callback.getResult();
		DbArrayList<T> list = null;
		if (resultList != null && resultList instanceof DbArrayList) {
			list = (DbArrayList<T>) resultList;
		}
		if (list != null) {
			list.setHeaderString(headerStr);
		}
		while (rs.next()) {
			T row = createRow(rs, resultSetColumnList);
			StringBuilder rowStr = createRowString(rs, resultSetColumnList);
			if (list != null) {
				list.addRowString(rowStr);
			}
			if (row != null) {
				callback.iterate(row);
			}
		}
	}

	private StringBuilder createHeaderString(List<ResultSetColumn> resultSetColumnList) {
		StringBuilder ret = new StringBuilder();
		for (ResultSetColumn column : resultSetColumnList) {
			if (isBinally(column) == true) {
				continue;
			}
			ret.append(trim(column.getName(), column.getDisplaySize()));
			ret.append("|");
		}
		if (0 < ret.length()) {
			ret.setLength(ret.length() - 1);
		}
		ret.append("\r\n");
		for (ResultSetColumn column : resultSetColumnList) {
			if (isBinally(column) == true) {
				continue;
			}
			ret.append(trim("------------------------------", column.getDisplaySize()));
			ret.append("-");
		}
		ret.append("\r\n");
		return ret;
	}

	private boolean isBinally(ResultSetColumn column) {
		return TypesUtil.getSQLType(column.getDataType()) == SQLTypes.BINARY;
	}

	private StringBuilder createRowString(ResultSet rs, List<ResultSetColumn> resultSetColumnList) throws SQLException {
		StringBuilder ret = new StringBuilder();
		JavaType<String> type = JavaTypes.STRING;
		for (ResultSetColumn column : resultSetColumnList) {
			if (isBinally(column) == true) {
				continue;
			}
			String value = type.getValue(rs, column.getName());
			if (value != null) {
				ret.append(trim(value.toString(), column.getDisplaySize()));
			} else {
				ret.append(trim("", column.getDisplaySize()));
			}
			ret.append("|");
		}
		if (0 < ret.length()) {
			ret.setLength(ret.length() - 1);
		}
		ret.append("\r\n");
		return ret;
	}

	private CharSequence trim(String str, int size) {
		StringBuilder ret = new StringBuilder();
		ret.append(str);
		if(size<12){
			size = 12;
		}
		while (ret.length() < size) {
			ret.append("             ");
		}
		return ret.subSequence(0, size);
	}

	protected List<ResultSetColumn> getResultSetColumnList(ResultSetMetaData rsmd) throws SQLException {
		int count = rsmd.getColumnCount();
		List<ResultSetColumn> ret = new ArrayList<ResultSetColumn>(count);

		for (int i = 0; i < count; ++i) {
			String columnName = rsmd.getColumnLabel(i + 1);
			int columnType = rsmd.getColumnType(i + 1);
			int columnDisplaySize = rsmd.getColumnDisplaySize(i + 1);
			ret.add(new ResultSetColumn(columnName, columnType, columnDisplaySize));
		}
		return ret;
	}

	protected abstract T createRow(ResultSet rs, List<ResultSetColumn> resultSetColumnList) throws SQLException;

	protected IterationCallback<T> getIterationCallback() {
		return callback;
	}
}
